--
--  Copyright 2022 The Open Islands Authors. All Rights Reserved.
--
--  Licensed under the Apache License, Version 2.0 (the "License");
--  you may not use this file except in compliance with the License.
--  You may obtain a copy of the License at
--
--      http://www.apache.org/licenses/LICENSE-2.0
--
--  Unless required by applicable law or agreed to in writing, software
--  distributed under the License is distributed on an "AS IS" BASIS,
--  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--  See the License for the specific language governing permissions and
--  limitations under the License.
--
local _M = {
    _VERSION = '0.1'
}

local string = require "string"
local resty_string = require "resty.string"
local json = require("cjson")
local http = require("resty.http")
local log = require("log")

local ngx = ngx
local table = table
local io_open = io.open

function _M.string_startswith(str, start)
    return string.sub(str, 1, string.len(start)) == start
end

function _M.string_split(str, delimiter)
    if str == nil or str == "" or delimiter == nil then
        return nil
    end

    local result = {}
    for match in (str..delimiter):gmatch("(.-)"..delimiter) do
        table.insert(result, match)
    end
    return result
end

function _M.string_to_hex(str)
    return resty_string.to_hex(str)
end

function _M.join_table(tb, delimiter)
    if tb == nil or tb == "" or delimiter == nil then
        return nil
    end
    return table.concat(tb, delimiter)
end

function _M.json_loads(str)
    return json.decode(str)
end

function _M.json_dumps(table)
    return json.encode(table)
end

function _M.read_file_data(path)
    local f, err = io_open(path, 'rb')

    if not f or err then
        return nil, err
    end

    local data = f:read('*all')
    f:close()
    return data, nil
end

function _M.http_client(url, method, params, headers, tries, ok_code)
    if not ok_code then
        ok_code = 200
    end
    local httpc = http.new()
    local res, err
    for t=1, tonumber(tries) do
        res, err = httpc:request_uri(url, {
            method = method,
            body = params,
            headers = headers
        })
        if res and res.status == ok_code then
            break
        end
        log.warning(string.format("try request url(%s) again.", url))
        ngx.sleep(0.05)
    end
    return res
end

function _M.if_module_available(name)
    if package.loaded[name] then
        return true
    else
        for _, searcher in ipairs(package.searchers or package.loaders) do
            local loader = searcher(name)
            if type(loader) == 'function' then
                package.preload[name] = loader
                return true
            end
        end
        return false
    end
end
function _M.get_zlib()
    local zlib
    if _M.if_module_available("zlib") then
        zlib = require("zlib")
    end
    return zlib
end

function _M.compress(content, level)
    local err = "success"
    local zlib = _M.get_zlib()
    if not zlib then
        return false, nil, "no zlib library"
    end
    if content then
        local stream = zlib.deflate(level)
        local deflated, eof, bytes_in, bytes_out = stream(content, "finish")
        log.debug("compress level: ", level, " bytes_in: ", bytes_in, " bytes_out: ", bytes_out, " ratio: ", (bytes_in - bytes_out) / bytes_in)
        return true, deflated, err
    else
        return false, nil, "content less than"
    end
end

function _M.decompress(content)
    local zlib = _M.get_zlib()
    if not zlib then
        return false, nil, "no zlib library"
    end
    if content and #content > 0 then
        local err = "success"
        local stream = zlib.inflate()
        local inflated, eof, bytes_in, bytes_out = stream(content)
        log.debug("decompress bytes_in: ", bytes_in, " bytes_out: ", bytes_out)
        return true, inflated, err
    else
        return false, nil, "content is empty"
    end
end

function _M.read_request_body()
    ngx.req.read_body()
    local body = ngx.req.get_body_data()
    if body == nil then
        local body_file = ngx.req.get_body_file()
        local err
        if body_file then
            body, err = _M.read_file_data(body_file)
            return body
        else
            return nil
        end
    else
        return body
    end
end


function _M.gen_sign(rand, timestamp, app_key, secret_key, body)
    local data = string.format("random%stimestamp%sappKey%sbody%ssecretKey%s", rand, timestamp, app_key, body, secret_key)
    local sha = sha256:new()
    sha:update(data)
    return tohex(sha:final())
end

function _M.set_access_header(user, body)
    local app_key = ngx.var.APP_KEY
    local secret_key = ngx.var.SECRET_KEY
    local rand = random.number(0, 1000000)
    local timestamp = os.time()
    local sign = _M.gen_sign(rand, timestamp, user, body, secret_key)
    ngx.req.set_header("APPKEY", app_key)
    ngx.req.set_header("RANDOM", rand)
    ngx.req.set_header("TIMESTAMP", timestamp)
    ngx.req.set_header("SIGN", sign)
end

return _M

